// 数据同步模块
// 负责拉取和存储期权数据

use crate::config::{DataSource, SyncConfig};
use akoption::db::OptionStore;
use anyhow::{anyhow, Context, Result};
use chrono::NaiveDate;
use serde::Serialize;
use std::time::Duration;
use tracing::{error, info, warn};

/// 数据同步器
#[derive(Serialize)]
pub struct DataSyncer {
    /// 配置
    pub config: SyncConfig,
    /// 数据存储
    #[serde(skip)]
    pub store: OptionStore,
}

impl DataSyncer {
    /// 创建新的数据同步器
    pub async fn new(config: SyncConfig) -> Result<Self> {
        let db_config = config.to_db_config();
        let store = OptionStore::new(db_config)
            .await
            .context("创建数据存储失败")?;
        
        Ok(Self { config, store })
    }
    
    /// 同步所有启用的数据源
    pub async fn sync_all(&self) -> Result<()> {
        info!("开始同步所有数据源");
        
        let sources = self.config.enabled_sources();
        let mut success_count = 0;
        let mut error_count = 0;
        
        for source in sources {
            match self.sync_source(source).await {
                Ok(_) => {
                    success_count += 1;
                    info!("数据源 {} 同步成功", source.name);
                }
                Err(e) => {
                    error_count += 1;
                    error!("数据源 {} 同步失败: {}", source.name, e);
                }
            }
        }
        
        info!(
            "数据同步完成: 成功 {}, 失败 {}",
            success_count, error_count
        );
        
        if error_count > 0 {
            Err(anyhow!("部分数据源同步失败"))
        } else {
            Ok(())
        }
    }
    
    /// 同步单个数据源
    async fn sync_source(&self, source: &DataSource) -> Result<()> {
        info!("同步数据源: {} ({})", source.name, source.description);
        info!("API函数: {}", source.api_function);
        info!("参数: {:?}", source.parameters);
        
        // 使用重试机制
        let retry_config = &self.config.retry;
        let mut last_error = None;
        
        for attempt in 0..=retry_config.max_retries {
            if attempt > 0 {
                warn!(
                    "重试数据源 {} (第 {} 次)",
                    source.name, attempt
                );
                tokio::time::sleep(Duration::from_secs(retry_config.retry_delay_seconds)).await;
            }
            
            match self.fetch_and_save(source).await {
                Ok(_) => return Ok(()),
                Err(e) => {
                    error!("尝试 {} 失败: {:?}", attempt + 1, e);
                    last_error = Some(e);
                }
            }
        }
        
        let final_error = last_error.unwrap();
        error!("所有重试失败，最终错误: {:?}", final_error);
        Err(final_error)
    }
    
    /// 拉取并保存数据
    async fn fetch_and_save(&self, source: &DataSource) -> Result<()> {
        // 根据 API 函数名调用相应的接口
        // 只保留测试脚本中验证通过的9个新浪接口
        match source.api_function.as_str() {
            // ============ 新浪财经接口 (上交所期权) ============
            
            // 1. 合约到期月份列表
            "option_sse_list_sina" => {
                let symbol = source.parameters.get("symbol")
                    .and_then(|v| Some(v.as_str()))
                    .unwrap_or("50ETF");
                let data = akoption::option_sse_list_sina(symbol)
                    .await
                    .context("获取期权到期月份列表失败")?;
                // 保存月份列表
                self.store.save_expire_dates(&data)
                    .await
                    .context("保存到期月份列表失败")?;
                info!("保存了 {} 个到期月份", data.len());
            }
            
            // 2. 合约到期日和剩余天数
            "option_sse_expire_day_sina" => {
                // 支持逗号分隔的多个标的：50ETF,300ETF,500ETF,科创50,科创板50
                let symbols_str = source
                    .parameters
                    .get("symbols")
                    .map(|s| s.as_str())
                    .unwrap_or("50ETF,300ETF,500ETF,科创50,科创板50");
                let symbols: Vec<&str> = symbols_str
                    .split(',')
                    .map(|s| s.trim())
                    .filter(|s| !s.is_empty())
                    .collect();

                for sym in symbols {
                    // 先取该标的的所有有效到期月份
                    let months = akoption::option_sse_list_sina(sym)
                        .await
                        .with_context(|| format!("获取标的 {} 的到期月份失败", sym))?;

                    let mut json_rows: Vec<serde_json::Value> = Vec::new();

                    for month in months.iter() {
                        // 对每个月份求到期日与剩余天数
                        let (expire_day, remainder_days) = akoption::option_sse_expire_day_sina(month, sym)
                            .await
                            .with_context(|| format!("获取标的 {} 月份 {} 的到期信息失败", sym, month))?;

                        json_rows.push(serde_json::json!({
                            "symbol": sym,
                            "trade_month": month,
                            "expire_day": expire_day,
                            "remainder_days": remainder_days
                        }));
                    }

                    // 以标的维度保存
                    self.store
                        .save_minute_data(&format!("expire_days_{}", sym), &json_rows)
                        .await
                        .with_context(|| format!("保存标的 {} 的到期信息失败", sym))?;

                    info!("标的 {} 保存了 {} 条到期信息", sym, json_rows.len());
                }
            }
            
            // 3. 所有合约的代码
            "option_sse_codes_sina" => {
                // 支持多个 symbol（看涨期权/看跌期权）与多个 underlying（510050,510300,510500,588000,588080）
                let symbol_str = source.parameters.get("symbol")
                    .map(|s| s.as_str())
                    .unwrap_or("看涨期权");
                let symbols: Vec<&str> = symbol_str
                    .split(',')
                    .map(|s| s.trim())
                    .filter(|s| !s.is_empty())
                    .collect();

                let underlying_str = source.parameters.get("underlying")
                    .map(|s| s.as_str())
                    .unwrap_or("510050");
                let underlyings: Vec<&str> = underlying_str
                    .split(',')
                    .map(|s| s.trim())
                    .filter(|s| !s.is_empty())
                    .collect();

                for sym in symbols.iter() {
                    for ul in underlyings.iter() {
                        // underlying -> 品种名称 映射，用于获取月份列表
                        let variety = match *ul {
                            "510050" => "50ETF",
                            "510300" => "300ETF",
                            "510500" => "500ETF",
                            "588000" => "科创50",
                            "588080" => "科创板50",
                            other => {
                                warn!("未知的 underlying: {}，默认映射为 50ETF", other);
                                "50ETF"
                            }
                        };

                        // 获取该品种所有有效月份
                        let months = akoption::option_sse_list_sina(variety)
                            .await
                            .with_context(|| format!("获取 {} 的到期月份失败", variety))?;

                        for month in months.iter() {
                            let data = akoption::option_sse_codes_sina(sym, month, ul)
                                .await
                                .with_context(|| format!("获取合约代码失败: symbol={}, underlying={}, month={}", sym, ul, month))?;

                            // 转换为JSON保存，附带维度信息
                            let json_data: Vec<serde_json::Value> = data.iter()
                                .map(|(idx, code)| serde_json::json!({
                                    "symbol": sym,
                                    "underlying": ul,
                                    "trade_month": month,
                                    "序号": idx,
                                    "期权代码": code
                                }))
                                .collect();

                            self.store
                                .save_minute_data(&format!("codes_{}_{}_{}", sym, ul, month), &json_data)
                                .await
                                .with_context(|| format!("保存合约代码失败: symbol={}, underlying={}, month={}", sym, ul, month))?;

                            info!("保存了 {} 条期权合约代码 (symbol={}, underlying={}, month={})", data.len(), sym, ul, month);
                        }
                    }
                }
            }
            
            // 4. 实时数据
            "option_sse_spot_price_sina" => {
                let contract_code = source.parameters.get("contract_code")
                    .and_then(|v| Some(v.as_str()))
                    .unwrap_or("10004355");
                let data = akoption::option_sse_spot_price_sina(contract_code)
                    .await
                    .context("获取期权实时数据失败")?;
                // 转换为JSON保存
                let json_data: Vec<serde_json::Value> = vec![serde_json::to_value(&data)?];
                self.store.save_minute_data(&format!("spot_{}", contract_code), &json_data)
                    .await
                    .context("保存期权实时数据失败")?;
                info!("保存了期权实时数据");
            }
            
            // 5. 期权标的物的实时数据
            "option_sse_underlying_spot_price_sina" => {
                let codes_str = source.parameters.get("underlying_code")
                    .map(|v| v.as_str())
                    .unwrap_or("sh510500,sh510300,sh510050,sh588000,sh588080");

                let codes: Vec<&str> = codes_str
                    .split(',')
                    .map(|s| s.trim())
                    .filter(|s| !s.is_empty())
                    .collect();

                let mut saved_count = 0usize;
                for code in codes {
                    let price = akoption::option_sse_underlying_spot_price_sina(code)
                        .await
                        .with_context(|| format!("获取期权标的物实时数据失败: {}", code))?;
                    let json_data = serde_json::json!({
                        "underlying_code": code,
                        "price": price
                    });
                    self.store
                        .save_minute_data(&format!("underlying_{}", code), &vec![json_data])
                        .await
                        .with_context(|| format!("保存标的物实时数据失败: {}", code))?;
                    saved_count += 1;
                }
                info!("保存了 {} 个标的的实时价格", saved_count);
            }
            
            // 6. 期权希腊字母信息表
            "option_sse_greeks_sina" => {
                let contract_code = source.parameters.get("contract_code")
                    .and_then(|v| Some(v.as_str()))
                    .unwrap_or("10004355");
                let data = akoption::option_sse_greeks_sina(contract_code)
                    .await
                    .context("获取期权希腊字母信息失败")?;
                // 转换为JSON保存
                let json_data: Vec<serde_json::Value> = vec![serde_json::to_value(&data)?];
                self.store.save_minute_data(&format!("greeks_{}", contract_code), &json_data)
                    .await
                    .context("保存希腊字母信息失败")?;
                info!("保存了期权希腊字母信息");
            }
            
            // 7. 期权行情分钟数据
            "option_sse_minute_sina" => {
                let symbol = source.parameters.get("symbol")
                    .and_then(|v| Some(v.as_str()))
                    .context("option_sse_minute_sina 需要 symbol 参数")?;
                let data = akoption::option_sse_minute_sina(symbol)
                    .await
                    .context("获取期权行情分钟数据失败")?;
                // 转换为JSON保存
                let json_data: Vec<serde_json::Value> = data.iter()
                    .map(|item| serde_json::to_value(item))
                    .collect::<Result<Vec<_>, _>>()?;
                self.store.save_minute_data(symbol, &json_data)
                    .await
                    .context("保存分钟行情数据失败")?;
                info!("保存了 {} 条期权行情分钟数据", data.len());
            }
            
            // 8. 期权行情日数据
            "option_sse_daily_sina" => {
                let symbol = source.parameters.get("symbol")
                    .and_then(|v| Some(v.as_str()))
                    .context("option_sse_daily_sina 需要 symbol 参数")?;
                let data = akoption::option_sse_daily_sina(symbol)
                    .await
                    .context("获取期权行情日数据失败")?;
                // 转换为JSON保存
                let json_data: Vec<serde_json::Value> = data.iter()
                    .map(|item| serde_json::to_value(item))
                    .collect::<Result<Vec<_>, _>>()?;
                self.store.save_daily_data(symbol, &json_data)
                    .await
                    .context("保存日线行情数据失败")?;
                info!("保存了 {} 条期权行情日数据", data.len());
            }
            
            // 9. 期权行情分时数据-新浪（完整字段）
            "option_finance_minute_sina" => {
                let symbol = source.parameters.get("symbol")
                    .and_then(|v| Some(v.as_str()))
                    .context("option_finance_minute_sina 需要 symbol 参数")?;
                let data = akoption::option_finance_minute_full_sina(symbol)
                    .await
                    .context("获取期权分时行情数据失败")?;
                // 转换为JSON保存
                let json_data: Vec<serde_json::Value> = data.iter()
                    .map(|item| serde_json::to_value(item))
                    .collect::<Result<Vec<_>, _>>()?;
                self.store.save_minute_data(&format!("finance_full_{}", symbol), &json_data)
                    .await
                    .context("保存分时行情数据失败")?;
                info!("保存了 {} 条期权分时行情数据", data.len());
            }
            
            // 其他接口
            _ => {
                warn!("未实现的 API 函数: {}", source.api_function);
                return Err(anyhow!("未实现的 API 函数: {}", source.api_function));
            }
        }
        
        Ok(())
    }
    
    /// 检查并补录上个交易日数据
    pub async fn check_and_backfill(&self, date: NaiveDate) -> Result<()> {
        info!("检查日期 {} 的数据", date);
        
        // TODO: 实现数据检查逻辑
        // 1. 查询数据库中是否有该日期的数据
        // 2. 如果没有，则拉取该日期的数据
        
        // 暂时直接同步所有数据
        self.sync_all().await
    }
    
    /// 获取数据存储引用
    #[allow(dead_code)]
    pub fn store(&self) -> &OptionStore {
        &self.store
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::{ScheduleConfig, DatabaseConfig, RetryConfig, LoggingConfig};
    use std::collections::HashMap;
    
    /// 创建测试用的配置
    fn create_test_config(sources: Vec<DataSource>) -> SyncConfig {
        // 使用临时目录创建测试数据库
        let temp_dir = std::env::temp_dir();
        let db_path = temp_dir.join(format!("test_akoption_{}.db", std::process::id()));
        
        SyncConfig {
            schedule: ScheduleConfig {
                daily_record_time: "15:30:00".to_string(),
                daily_check_time: "08:00:00".to_string(),
            },
            database: DatabaseConfig {
                path: db_path.to_string_lossy().to_string(),
                namespace: "test".to_string(),
                database: "options_test".to_string(),
            },
            sources,
            retry: RetryConfig {
                max_retries: 2,
                retry_delay_seconds: 1,
                timeout_seconds: 30,
            },
            logging: LoggingConfig {
                level: "info".to_string(),
                log_file: "./test.log".to_string(),
            },
        }
    }
    
    /// 创建测试用的数据源
    fn create_test_source(name: &str, enabled: bool, api_function: &str) -> DataSource {
        DataSource {
            name: name.to_string(),
            description: format!("Test source: {}", name),
            api_function: api_function.to_string(),
            enabled,
            sync_type: "snapshot".to_string(),
            table: format!("test_table_{}", name),
            parameters: HashMap::new(),
        }
    }
    
    #[tokio::test]
    async fn test_syncer_creation() {
        let config = SyncConfig::from_file("../../../config/sync_config.toml");
        if let Ok(config) = config {
            let syncer = DataSyncer::new(config).await;
            assert!(syncer.is_ok());
        }
    }
    
    /// 测试 sync_all: 所有数据源都成功
    /// 注意：此测试依赖真实的网络 API 调用，可能因网络问题而失败
    #[tokio::test]
    #[ignore] // 标记为 ignore，因为依赖外部 API
    async fn test_sync_all_all_success() {
        // 创建配置，包含多个启用的数据源（使用新浪接口）
        let sources = vec![
            create_test_source("source1", true, "option_sse_list_sina"),
            create_test_source("source2", true, "option_sse_expire_day_sina"),
        ];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：所有数据源都成功时，应该返回 Ok
        // 注意：由于依赖外部 API，此测试可能不稳定
        if result.is_err() {
            eprintln!("警告: API 调用失败，这可能是由于网络问题: {:?}", result.err());
        }
    }
    
    /// 测试 sync_all: 部分数据源失败
    /// 注意：此测试依赖真实的网络 API 调用
    #[tokio::test]
    #[ignore] // 标记为 ignore，因为依赖外部 API
    async fn test_sync_all_partial_failure() {
        // 创建配置，包含一个有效的数据源和一个无效的数据源
        let sources = vec![
            create_test_source("valid_source", true, "option_sse_list_sina"),
            create_test_source("invalid_source", true, "non_existent_api"),
        ];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：部分数据源失败时，应该返回 Err
        assert!(result.is_err(), "部分数据源失败时应该返回 Err");
        assert!(
            result.unwrap_err().to_string().contains("部分数据源同步失败"),
            "错误信息应该包含'部分数据源同步失败'"
        );
    }
    
    /// 测试 sync_all: 所有数据源都失败
    #[tokio::test]
    async fn test_sync_all_all_failure() {
        // 创建配置，包含多个无效的数据源
        let sources = vec![
            create_test_source("invalid1", true, "non_existent_api_1"),
            create_test_source("invalid2", true, "non_existent_api_2"),
        ];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：所有数据源都失败时，应该返回 Err
        assert!(result.is_err(), "所有数据源失败时应该返回 Err");
    }
    
    /// 测试 sync_all: 没有启用的数据源
    #[tokio::test]
    async fn test_sync_all_no_enabled_sources() {
        // 创建配置，所有数据源都未启用
        let sources = vec![
            create_test_source("disabled1", false, "option_sse_list_sina"),
            create_test_source("disabled2", false, "option_sse_expire_day_sina"),
        ];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：没有启用的数据源时，应该返回 Ok（没有错误）
        assert!(result.is_ok(), "没有启用的数据源时应该返回 Ok");
    }
    
    /// 测试 sync_all: 空数据源列表
    #[tokio::test]
    async fn test_sync_all_empty_sources() {
        // 创建配置，数据源列表为空
        let sources = vec![];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：空数据源列表时，应该返回 Ok
        assert!(result.is_ok(), "空数据源列表时应该返回 Ok");
    }
    
    /// 测试 sync_all: 混合启用和禁用的数据源
    /// 注意：此测试依赖真实的网络 API 调用
    #[tokio::test]
    #[ignore] // 标记为 ignore，因为依赖外部 API
    async fn test_sync_all_mixed_enabled_disabled() {
        // 创建配置，包含启用和禁用的数据源
        let sources = vec![
            create_test_source("enabled1", true, "option_sse_list_sina"),
            create_test_source("disabled1", false, "option_sse_expire_day_sina"),
            create_test_source("enabled2", true, "option_sse_codes_sina"),
            create_test_source("disabled2", false, "non_existent_api"),
        ];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：只有启用的数据源会被同步，禁用的数据源（包括无效的）会被忽略
        // 注意：由于依赖外部 API，此测试可能不稳定
        if result.is_err() {
            eprintln!("警告: API 调用失败，这可能是由于网络问题: {:?}", result.err());
        }
    }
    
    /// 测试 sync_all: 验证成功和失败计数
    /// 注意：此测试依赖真实的网络 API 调用
    #[tokio::test]
    #[ignore] // 标记为 ignore，因为依赖外部 API
    async fn test_sync_all_counts() {
        // 创建配置，包含2个有效和1个无效的数据源
        let sources = vec![
            create_test_source("valid1", true, "option_sse_list_sina"),
            create_test_source("valid2", true, "option_sse_expire_day_sina"),
            create_test_source("invalid", true, "non_existent_api"),
        ];
        let config = create_test_config(sources);
        
        // 创建同步器
        let syncer = DataSyncer::new(config).await;
        assert!(syncer.is_ok(), "同步器创建应该成功");
        
        let syncer = syncer.unwrap();
        
        // 执行同步
        let result = syncer.sync_all().await;
        
        // 验证结果：有失败的数据源，应该返回 Err
        assert!(result.is_err(), "有失败的数据源时应该返回 Err");
        
        // 注意：实际的成功/失败计数会在日志中输出
        // 这里我们验证错误信息
        let error_msg = result.unwrap_err().to_string();
        assert!(
            error_msg.contains("部分数据源同步失败"),
            "错误信息应该指示部分数据源失败"
        );
    }
}

// 手动实现 Clone trait
impl Clone for DataSyncer {
    fn clone(&self) -> Self {
        // 只克隆配置，不克隆数据存储
        Self {
            config: self.config.clone(),
            // 返回一个新的配置对象，在使用时需要重新创建数据存储
            store: self.store.clone(),
        }
    }
}
